package com.jacoco.incremental;

import com.jacoco.incremental.config.ConfigConstant;
import com.jacoco.incremental.config.JacocoConfig;
import com.jacoco.incremental.git.MyGit;
import com.jacoco.incremental.source.ScriptResource;
import com.jacoco.incremental.utils.Empty;
import com.jacoco.incremental.utils.JacocoCMD;
import com.jacoco.incremental.utils.ViewInfo;
import com.jacoco.incremental.utils.xml.ConfigXml;
import org.yaml.snakeyaml.Yaml;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;

/**
 * 增量统计入口
 */
public class JacocoMain {
    private File ymlFile;

    public JacocoMain setYmlFile(File ymlFile) {
        this.ymlFile = ymlFile;
        return this;
    }

    public void build() {
        try {
            new Build().execute();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    class Build {
        private JacocoConfig config;
        private boolean canRun=true;

        private Build() {
        }

        private void execute() throws IOException {
            setConfig();
            if (!canRun) return;
            ViewInfo.info("clone from git ... ");
            new MyGit().setConfig(config).build();
            new ConfigXml().setConfig(config).build();
            try {

                JacocoCMD cmd = new JacocoCMD();
                boolean success = true;

                if (this.config.isJacocoConfigBuild()) {
                    String compile = "gradle".equals(this.config.getJacocoConfigPom())
                            ? "./gradlew build"
                            : "mvn clean compile";
                    ViewInfo.info("{},编译源码中，稍等片刻...", compile);
                    success = cmd.exec(compile.split(" "), new File(config.getJacocoPathSource()));
                }
                if (success) {
                    if (Empty.isNotEmpty(this.config.getJacocoPathExec())) {
                        ViewInfo.info("获取jacoco ec文件,{}", this.config.getJacocoPathExec());
                    } else {
                        success = cmd.exec(("ant -f " + config.getJacocoPathSource() + "/build.xml dump").split(" "));
                    }
                }
                if (success) {
                    ViewInfo.info("生成jacoco覆盖率统计报告...");
                    cmd.exec(("ant -f " + config.getJacocoPathSource() + "/build.xml report").split(" "));
                    new ScriptResource().copybuild(config);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                File file = new File(config.getJacocoPathSource() + "/jacocoant.jar");
                File xmlFile = new File(config.getJacocoPathSource() + "/build.xml");
                if (file.exists()) file.delete();
                if (xmlFile.exists()) xmlFile.delete();
            }
        }

        /**
         * 设置配置文件
         *
         * @return
         * @throws IOException
         */
        private Build setConfig() throws IOException {
            LinkedHashMap ymlMap = new Yaml().loadAs(new FileInputStream(ymlFile), LinkedHashMap.class);
//            checkConfig(ymlMap);
            if (this.canRun) {
                this.config = JacocoConfig.getJacocoConfig();
                supplementJacocoConfig(ymlMap);
            }
            return this;
        }

        /**
         * 将bean补充完整
         *
         * @param ymlMap
         */
        private void supplementJacocoConfig(LinkedHashMap ymlMap) {
            LinkedHashMap<String, Object> map = (LinkedHashMap<String, Object>) ymlMap.get(ConfigConstant.V1_JACOCO);
            LinkedHashMap<String, Object> config = (LinkedHashMap<String, Object>) map.get(ConfigConstant.V2_CONFIG);
            LinkedHashMap<String, Object> git = (LinkedHashMap<String, Object>) map.get(ConfigConstant.V2_GIT);
            LinkedHashMap<String, Object> path = (LinkedHashMap<String, Object>) map.get(ConfigConstant.V2_PATH);
            LinkedHashMap<String, Object> server = (LinkedHashMap<String, Object>) map.get(ConfigConstant.V2_SERVER);

            this.config
                    .setJacocoConfigBuild(config != null
                            ? Boolean.valueOf(config.get(ConfigConstant.V3_CONFIG_BUILD).toString())
                            : true)
                    .setJacocoConfigPom(config != null
                            ? config.get(ConfigConstant.V3_CONFIG_POM).toString()
                            : "gradle")
                    .setJacocoPathExec(path.get(ConfigConstant.V3_PATH_EXEC).toString())
                    .setJacocoPathReport(path.get(ConfigConstant.V3_PATH_REPORT).toString())
                    .setJacocoPathSource(path.get(ConfigConstant.V3_PATH_SOURCE).toString())
                    .setJacocoServerIp(server.get(ConfigConstant.V3_SERVER_IP).toString())
                    .setJacocoServerPort(server.get(ConfigConstant.V3_SERVER_PORT).toString())
                    .setJacocoGitUrl(git.get(ConfigConstant.V3_GIT_URL).toString())
                    .setJacocoGitUser(git.get(ConfigConstant.V3_GIT_USER).toString())
                    .setJacocoGitPswd(git.get(ConfigConstant.V3_GIT_PSWD).toString());

            if (git.containsKey(ConfigConstant.V3_GIT_BRANCH)
                    && Empty.isNotEmpty(git.get(ConfigConstant.V3_GIT_BRANCH))) {
                this.config.setJacocoGitBranch(git.get(ConfigConstant.V3_GIT_BRANCH).toString());
            }

            if (git.containsKey(ConfigConstant.V3_GIT_INCLUDE)
                    && Empty.isNotEmpty(git.get(ConfigConstant.V3_GIT_INCLUDE))) {
                List<String> list = new ArrayList<>();
                String[] s = git.get(ConfigConstant.V3_GIT_INCLUDE).toString().split(",");
                for (String t : s) {
                    list.add(t);
                }
                this.config.setJacocoGitInclude(list);
            }

            if (git.containsKey(ConfigConstant.V3_GIT_EXCLUDE)
                    && Empty.isNotEmpty(git.get(ConfigConstant.V3_GIT_EXCLUDE))) {
                this.config.setJacocoGitExculde(Arrays.asList(git.get(ConfigConstant.V3_GIT_EXCLUDE).toString().split(",")));
            }

            if (git.containsKey(ConfigConstant.V3_GIT_SOURCE)
                    && Empty.isNotEmpty(git.get(ConfigConstant.V3_GIT_SOURCE))) {
                String source = git.get(ConfigConstant.V3_GIT_SOURCE).toString();
                if (Empty.isNotEmpty(source)) {
                    if (source.indexOf(",") > -1) {
                        String[] sources = source.split(",");
                        this.config.setJacocoGitSourcePath(sources);
                    } else {
                        this.config.setJacocoGitSourcePath(new String[]{source});
                    }
                }
            }

            if (git.containsKey(ConfigConstant.V3_GIT_CLASSES)
                    && Empty.isNotEmpty(git.get(ConfigConstant.V3_GIT_CLASSES))) {
                String classes = git.get(ConfigConstant.V3_GIT_CLASSES).toString();
                if (Empty.isNotEmpty(classes)) {
                    this.config.setJacocoGitBuildTarget(classes.split(","));
                }
            }

            if (git.containsKey(ConfigConstant.V3_GIT_VERSION1)
                    && Empty.isNotEmpty(git.get(ConfigConstant.V3_GIT_VERSION1))) {
                this.config.setJacocoGitV1(git.get(ConfigConstant.V3_GIT_VERSION1).toString());
            }

            if (git.containsKey(ConfigConstant.V3_GIT_VERSION2)
                    && Empty.isNotEmpty(git.get(ConfigConstant.V3_GIT_VERSION2))) {
                this.config.setJacocoGitV2(git.get(ConfigConstant.V3_GIT_VERSION2).toString());
            }
        }

        /**
         * 检查配置文件完整性
         *
         * @param ymlMap
         * @return
         */
        private void checkConfig(LinkedHashMap<String, Object> ymlMap) {
            if (ymlMap.containsKey(ConfigConstant.V1_JACOCO)) {
                LinkedHashMap<String, Object> yml = (LinkedHashMap<String, Object>) ymlMap.get(ConfigConstant.V1_JACOCO);
                if (yml.size() > 2) {
                    List<String> v2Keys = new ArrayList<String>();
                    for (String key : yml.keySet()) {
                        // config build 默认true，可不写
                        if (key.equals("config")) continue;
                        v2Keys.add(key);
                        if (!hasV3Key(key, (Map<String, Object>) yml.get(key))) break;
                    }
                    if (v2Keys.indexOf(ConfigConstant.V2_GIT) > -1
                            && v2Keys.indexOf(ConfigConstant.V2_PATH) > -1
                            && v2Keys.indexOf(ConfigConstant.V2_SERVER) > -1) {
                        return;
                    }
                }
            }
            ViewInfo.error("请检查参数是否正确");
            canRun = false;
        }

        /**
         * 1: git
         * 2: path
         * 3: server
         * 0: 不存在
         *
         * @param key
         * @return
         */
        private boolean hasV3Key(String key, Map<String, Object> v2Map) {
            int bz = key.equals(ConfigConstant.V2_GIT) ? 1 :
                    key.equals(ConfigConstant.V2_PATH) ? 2 :
                            key.equals(ConfigConstant.V2_SERVER) ? 3 : 0;

            switch (bz) {
                case 1:
                    this.canRun =
                            v2Map.containsKey(ConfigConstant.V3_GIT_URL) &&
                                    v2Map.containsKey(ConfigConstant.V3_GIT_USER) &&
                                    v2Map.containsKey(ConfigConstant.V3_GIT_PSWD)
                                    && Empty.isNotEmpty(v2Map.get(ConfigConstant.V3_GIT_URL))
                                    && Empty.isNotEmpty(v2Map.get(ConfigConstant.V3_GIT_USER))
                                    && Empty.isNotEmpty(v2Map.get(ConfigConstant.V3_GIT_PSWD));
                    break;
                case 2:
                    this.canRun =
                            v2Map.containsKey(ConfigConstant.V3_PATH_REPORT) &&
                                    v2Map.containsKey(ConfigConstant.V3_PATH_SOURCE)
                                    && Empty.isNotEmpty(v2Map.get(ConfigConstant.V3_PATH_REPORT))
                                    && Empty.isNotEmpty(v2Map.get(ConfigConstant.V3_PATH_SOURCE));

                    break;
                case 3:
                    this.canRun =
                            v2Map.containsKey(ConfigConstant.V3_SERVER_IP) &&
                                    v2Map.containsKey(ConfigConstant.V3_SERVER_PORT)
                                    && Empty.isNotEmpty(v2Map.get(ConfigConstant.V3_SERVER_IP))
                                    && Empty.isNotEmpty(v2Map.get(ConfigConstant.V3_SERVER_PORT));
                    break;
            }
            return this.canRun;
        }
    }
}
